package rollup

import (
	"crypto/ecdsa"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"io/ioutil"
	"strconv"

	"github.com/consensys/gnark-crypto/ecc/bn254/fr"
	"github.com/consensys/gnark/frontend"
)

const (
	SizeAccount = 120
)

// AccountConstraints accounts encoded as constraints
type AccountConstraints struct {
	Credit frontend.Variable `gnark:"-"`
	Index  frontend.Variable
	Nonce  frontend.Variable
	Data   frontend.Variable `gnark:"-"`
	PubKey ecdsa.PublicKey   `gnark:"-"`
}

type Account struct {
	Credit uint64
	Index  uint64          // 8 byte
	Nonce  uint64          // 8 byte
	Data   fr.Element      // 32 byte
	PubKey ecdsa.PublicKey // 64 byte
}

func NewAccount(userId uint64) *Account {
	var acc Account
	acc.Credit = 0
	acc.Index = userId - 1
	acc.Nonce = 0
	acc.Data.SetZero()
	acc.PubKey = *GetPubKey(int(userId))
	return &acc
}

func (ac *Account) Serialize() []byte {

	var res [SizeAccount]byte
	binary.BigEndian.PutUint64(res[0:], ac.Credit)
	binary.BigEndian.PutUint64(res[8:], ac.Index)
	binary.BigEndian.PutUint64(res[16:], ac.Nonce)

	buf := ac.Data.Bytes()
	copy(res[24:], buf[:])

	buf1 := ac.PubKey.X.Bytes()
	copy(res[56:], buf1[:])
	buf1 = ac.PubKey.Y.Bytes()
	copy(res[88:], buf1[:])
	return res[:]
}

func DeserializeAccount(data []byte) (res *Account, err error) {
	res = &Account{}
	res.Credit = binary.BigEndian.Uint64(data[0:])
	res.Index = binary.BigEndian.Uint64(data[8:16])
	res.Nonce = binary.BigEndian.Uint64(data[16:24])
	res.Data.SetBytes(data[24:56])

	res.PubKey = *GetPubKey(int(1))
	res.PubKey.X.SetBytes(data[56:88])
	res.PubKey.Y.SetBytes(data[88:])
	return
}

func GetPubKey(userId int) *ecdsa.PublicKey {
	userCertDir := "/mnt/e/fabricdev/fabric-samples/test-network/organizations/peerOrganizations/org1.example.com/users/User" +
		strconv.Itoa(userId) +
		"@org1.example.com/msp/signcerts/User" +
		strconv.Itoa(userId) +
		"@org1.example.com-cert.pem"

	pemPubKey, _ := ioutil.ReadFile(userCertDir)
	block, _ := pem.Decode(pemPubKey)
	cert, _ := x509.ParseCertificate(block.Bytes)
	return cert.PublicKey.(*ecdsa.PublicKey)
}
